//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-02-01
//
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using JetBrains.Annotations;
using LargoCommon.Abstract;
using LargoCommon.Midi;
namespace LargoCommon.Music
{
///
/// Musical Body.
///
public class MusicalBody
{
#region Fields
///
/// Musical Bars.
///
private List bars;
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The given context.
public MusicalBody(MusicalContext givenContext) {
this.Context = givenContext;
}
///
/// Initializes a new instance of the class.
///
/// The given strip.
public MusicalBody(MusicalStrip givenStrip) {
this.Context = givenStrip.Context;
this.Context.Header.NumberOfLines = givenStrip.Lines.Count;
this.PrepareElements(givenStrip);
}
#endregion
#region Properties - Xml
/// Gets Xml representation.
/// Property description.
public virtual XElement GetXElement {
get {
XElement xbody = new XElement("Body");
//// Bars
XElement xbars = new XElement("Bars");
//// Musical Bars to XML
foreach (MusicalBar mbar in this.Bars.Where(mbar => mbar != null)) {
//// mtrack.MusicalBlock = givenMusicalBlock;
var xbar = mbar.GetXElement;
xbars.Add(xbar);
}
//// var xtrack = MusicalTrackPort.WriteMusicalTrack(givenElement.Track); xelement.Add(xtrack);
xbody.Add(xbars);
return xbody;
}
}
#endregion
#region Properties
///
/// Gets or sets the context.
///
///
/// The context.
///
public MusicalContext Context { get; set; }
/// Gets list of bars.
/// Property description.
public IList Bars {
get {
Contract.Ensures(Contract.Result>() != null);
return this.bars ?? (this.bars = new List());
}
//// private set { this.musicalBars = value; }
}
///
/// Gets or sets the tempo events.
///
///
/// The tempo events.
///
public IEnumerable TempoEvents { get; set; } //// private
/// Gets string with harmony.
/// General musical property.
/// Returns value.
[UsedImplicitly]
public string HarmonyToString {
get {
var s = new StringBuilder();
s.Append(" Bar Modality Harmony(Duration) \n");
s.Append("-------------------------------------------------------------------\n");
var bs = from b in this.Bars orderby b.BarNumber select b;
foreach (var bar in bs.Where(bar => bar != null && !bar.IsEmpty)) {
s.Append(bar.NumberToString());
s.Append(bar.HarmonicBar.ModalityToString);
s.Append(bar.PureChordsToString + "\n");
}
return s.ToString();
}
}
/// Gets string of clusters.
/// General musical property.
/// Returns value.
[UsedImplicitly]
public string ClustersToString {
get {
var s = new StringBuilder();
s.Append(" Bar Cluster Harmony (Modality) \n");
s.Append("-------------------------------------------------------------------\n");
var bs = from b in this.Bars orderby b.BarNumber select b;
foreach (var bar in bs.Where(bar => bar != null && !bar.IsEmpty)) {
s.Append(bar.ClustersToString + "\n");
}
return s.ToString();
}
}
///
/// Gets the editor elements.
///
///
/// The editor elements.
///
public IList AllElements {
get {
var list = new List();
foreach (var bar in this.Bars) {
list.AddRange(bar.Elements);
}
//// var selectedList = (from element in list orderby element.Point.BarNumber,
//// element.Point.LineIndex select element).ToList();
return list;
}
}
///
/// Gets the line chunks.
///
///
/// The line chunks.
///
[UsedImplicitly]
public IList LineChunks {
get {
var list = new List();
for (int trackIdx = 0; trackIdx < this.Context.Header.NumberOfLines; trackIdx++) {
var elem = this.FistPlayingElementInTrack(trackIdx);
if (elem == null) {
continue;
}
var instrument = elem.Status.Instrument;
//// if (instrument == null) { instrument = elem.Line.Status.Instrument; }
var lineChunk = new PackLine(
this.Context.Header,
elem.Status.LineType,
LineRhythm.HarmonicShape,
instrument,
elem.Status.Octave,
elem.Status.Loudness,
elem.Status.MelodicFunction,
elem.Status.MelodicShape,
1);
list.Add(lineChunk);
}
return list;
}
}
#endregion
#region Public static
///
/// Sets the purpose to elements.
///
/// The given purpose.
/// The given elements.
public static void SetPurposeToElements(LinePurpose givenPurpose, IEnumerable givenElements) {
foreach (var element in givenElements) {
if (element?.Status != null) {
if (givenPurpose == LinePurpose.Fixed && (!element.MusicalLine.HasContent || element.MusicalLine.IsEmpty)) {
continue;
}
element.Status.LocalPurpose = givenPurpose;
}
}
}
///
/// Sets the instrument to elements.
///
/// The given instrument.
/// The given elements.
public static void SetInstrumentToElements(MusicalInstrument givenInstrument, IEnumerable givenElements) {
foreach (var element in givenElements) {
if (element?.Status != null) {
//// if (element.MusicalLine.IsEmpty)) { continue; }
element.Status.Instrument = givenInstrument;
}
}
}
///
/// Sets the octave to elements.
///
/// The given octave.
/// The given elements.
public static void SetOctaveToElements(MusicalOctave givenOctave, IEnumerable givenElements) {
foreach (var element in givenElements) {
if (element?.Status != null) {
//// if (element.MusicalLine.IsEmpty)) { continue; }
element.Status.Octave = givenOctave;
}
}
}
///
/// Sets the loudness to elements.
///
/// The given loudness.
/// The given elements.
public static void SetLoudnessToElements(MusicalLoudness givenLoudness, IEnumerable givenElements) {
foreach (var element in givenElements) {
if (element?.Status != null) {
//// if (element.MusicalLine.IsEmpty)) { continue; }
element.Status.Loudness = givenLoudness;
}
}
}
#endregion
#region Rhythmic
///
/// Rhythmic of harmony.
///
/// Returns value.
[UsedImplicitly]
public IList RhythmicOfHarmony() {
var structs = new List();
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var bar1 in this.Bars.Where(bar1 => bar1 != null)) {
if (bar1.HarmonicBar == null) {
continue;
}
var rstruct = bar1.HarmonicBar.RhythmicStructure; //// Clone?
structs.Add(rstruct);
}
return structs;
}
///
/// Reorders the line rhythmic.
///
/// The line number.
/// The block model.
[UsedImplicitly]
public void SetRandomRhythm(int lineIndex, RhythmicModel model) {
Contract.Requires(model != null);
var elements = this.ElementsOfLine(lineIndex);
var master = new ElementMaster(elements);
var structures = model.RhythmicStructuresOfMotives;
master.SetRandomRhythm(structures);
}
#endregion
#region Reorder line properties
///
/// Reorders the line rhythmic.
///
/// The line number.
public void ReorderLineRhythmic(int lineIndex) {
var elements = this.ElementsOfLine(lineIndex);
var master = new ElementMaster(elements);
master.ReorderRhythmic();
}
///
/// Reorders the line rhythmic.
///
/// The line number.
public void ReorderLineMelodic(int lineIndex) {
var elements = this.ElementsOfLine(lineIndex);
//// var musicalEditorElements = elements as IList ?? elements.ToList();
var master = new ElementMaster(elements);
master.ReorderMelodic();
}
///
/// Reorders the line rhythmic.
///
public void ReorderLineHarmonic() {
foreach (var bar1 in this.Bars) {
if (bar1?.HarmonicBar == null) {
continue;
}
var bar1C = bar1;
foreach (var bar2 in this.Bars.Where(bar2 => bar2.HarmonicBar != null && bar2.BarNumber > bar1C.BarNumber).Where(bar2 => MathSupport.RandomNatural(10) < 3)) {
bar1.SwapHarmonyWith(bar2);
}
}
}
#endregion
#region String representation
/// String representation of the object.
/// Returns value.
public override string ToString() {
var s = new StringBuilder();
s.AppendFormat("MusicalBody (Length {0})", this.Bars.Count);
return s.ToString();
}
#endregion
///
/// Gets the sorted bars.
///
/// Type of the line.
/// Returns value.
///
/// The sorted bars.
///
public IList SortedBars(MusicalLineType lineType) {
Contract.Ensures(Contract.Result>() != null);
var bs = this.Bars.ToList();
foreach (var bar in bs) {
bar.ResetValuesOfTicks();
}
bs.Sort(new BarComparer(lineType));
return bs;
}
#region Public methods - Elements
///
/// Marks the rhythmic motive.
///
/// The given motive.
/// The given area.
public void MarkRhythmicMotive(RhythmicMotive givenMotive, MusicalArea givenArea) {
for (int barNumber = givenArea.StartPoint.BarNumber; barNumber <= givenArea.EndPoint.BarNumber; barNumber++) {
var bar = this.GetBar(barNumber);
if (givenArea.StartPoint.LineIndex >= bar?.Elements.Count) { ////2019/12 (?)
continue;
}
var element = bar?.Elements[givenArea.StartPoint.LineIndex];
if (element?.Status == null) {
continue;
}
element.Status.RhythmicMotive = givenMotive;
}
}
///
/// Marks the melodic motive.
///
/// The given motive.
/// The given area.
public void MarkMelodicMotive(MelodicMotive givenMotive, MusicalArea givenArea) {
for (int barNumber = givenArea.StartPoint.BarNumber; barNumber <= givenArea.EndPoint.BarNumber; barNumber++) {
var bar = this.GetBar(barNumber);
if (givenArea.StartPoint.LineIndex >= bar?.Elements.Count) { ////2019/12 (?)
continue;
}
var element = bar?.Elements[givenArea.StartPoint.LineIndex];
if (element?.Status == null) {
continue;
}
element.Status.MelodicMotive = givenMotive;
}
}
///
/// Gets the bar.
///
/// The given bar number.
/// Returns value.
public MusicalBar GetBar(int givenBarNumber) {
var barIdx = givenBarNumber - 1;
if (barIdx < 0 || barIdx >= this.Bars.Count) {
return null;
}
var bar = this.Bars[barIdx];
return bar;
}
///
/// Prepares the rhythm in line.
///
/// Index of the given line.
public void PrepareRhythmInLine(int givenLineIndex) {
foreach (var bar in this.Bars) {
var point = MusicalPoint.GetPoint(givenLineIndex, bar.BarNumber);
var me = this.GetElement(point);
if (me == null) {
continue;
}
if (me.Status.RhythmicStructure != null) {
me.Status.RhythmicMotive = RhythmicMotive.SimpleRhythmicMotive(me.Status.RhythmicStructure);
//// me.Status.RhythmicMotive = me.Status.RhythmicMotive;
}
me.FillWithRequestedRhythm();
}
}
///
/// Prepares the elements.
///
/// The given strip.
public void PrepareElements(MusicalStrip givenStrip) {
var h = this.Context.Header;
var numberOfTracks = h.NumberOfLines; //// 2016 givenStrip. Lines .Count;
var numberOfBars = h.NumberOfBars;
var barMidiDuration = MusicalProperties.BarMidiDuration(
h.System.RhythmicOrder,
h.Metric.MetricBeat,
h.Metric.MetricGround,
h.Division);
MusicalBar lastBar = null;
for (int barNumber = 1; barNumber <= numberOfBars; barNumber++) {
MusicalBar bar = new MusicalBar(barNumber, this) {
BarNumber = barNumber,
PreviousBar = lastBar,
TimePoint = (barNumber - 1) * barMidiDuration
};
bar.SetElements(new List());
if (lastBar != null) {
lastBar.NextBar = bar;
}
for (byte lineIndex = 0; lineIndex < numberOfTracks; lineIndex++) {
var line = givenStrip.Lines.ElementAt(lineIndex);
var element = new MusicalElement(line.FirstStatus, null) {
Bar = bar,
Line = line
};
element.SetTones(line.MusicalTonesInBar(barNumber));
element.Status.BarNumber = barNumber; //// 2019/01
element.Status.MelodicVariety = line.FirstStatus.MelodicVariety; //// 2019/01
element.Status.MelodicPlan = new MelodicPlan(element.Tones); //// 2016/10 !?!?!?!?
if (!element.Tones.HasAnySoundingTone) {
element.Status.LocalPurpose = LinePurpose.None;
}
bar.Elements.Add(element);
}
this.Bars.Add(bar);
lastBar = bar;
}
}
///
/// Sets the purpose to all elements.
///
/// The given purpose.
public void SetPurposeToAllElements(LinePurpose givenPurpose) {
MusicalBody.SetPurposeToElements(givenPurpose, this.AllElements);
}
///
/// Gets the element.
///
/// The given point.
///
/// Returns value.
///
public MusicalElement GetElement(MusicalPoint givenPoint) {
MusicalElement element = null;
var barIndex = givenPoint.BarNumber - 1;
if (barIndex >= 0 && barIndex < this.Bars.Count) {
var bar = this.Bars[barIndex];
var lineIndex = givenPoint.LineIndex;
if (lineIndex >= 0 && lineIndex < bar.Elements.Count) {
element = bar.Elements[lineIndex];
}
}
return element;
}
///
/// Editors the elements of line.
///
/// The line number.
/// Returns value.
public IEnumerable ElementsOfLine(int lineIndex) {
var list = (from v in this.AllElements
where v.Point.LineIndex == lineIndex
select v).ToList();
//// var selectedList = (from element in list orderby element.Point.BarNumber
//// select element).ToList();
return list;
}
///
/// Elements the of line.
///
/// The track identifier.
/// Returns value.
public IEnumerable ElementsOfLine(Guid lineIdent) {
var list = (from v in this.AllElements
where v.Line.LineIdent == lineIdent
select v).ToList();
//// var selectedList = (from element in list orderby element.Point.BarNumber
//// select element).ToList();
return list;
}
/// Enumerates rhythmic structures of line in this collection.
/// The track identifier.
///
/// An enumerator that allows foreach to be used to process rhythmic structures of line in this
/// collection.
///
[UsedImplicitly]
public IEnumerable RhythmicStructuresOfLine(Guid lineIdent) {
var list = (from v in this.AllElements
where v.Line.LineIdent == lineIdent && v.Status?.RhythmicStructure != null
select v.Status.RhythmicStructure).ToList();
//// var selectedList = (from element in list orderby element.Point.BarNumber
//// select element).ToList();
return list;
}
///
/// Clones the line elements.
///
/// The line from.
/// The line to.
[UsedImplicitly]
public void CopyLineStatus(int lineFrom, int lineTo) {
foreach (var bar in this.Bars) {
var pointFrom = MusicalPoint.GetPoint(lineFrom, bar.BarNumber);
var elementFrom = this.GetElement(pointFrom);
var pointTo = MusicalPoint.GetPoint(lineTo, bar.BarNumber);
var elementTo = this.GetElement(pointTo);
elementTo.Status = (LineStatus)elementFrom.Status.Clone();
}
}
///
/// Determines whether [is in editor] [the specified line].
///
/// Index of the line.
/// The bar.
///
/// true if [is in editor] [the specified line]; otherwise, false.
///
[UsedImplicitly]
public bool IsInEditor(int lineIndex, int bar) {
return lineIndex >= 0 && bar >= 1
&& lineIndex < this.Context.Header.NumberOfLines
&& bar <= this.Context.Header.NumberOfBars;
}
///
/// Alters the octaves.
///
/// The line number.
public void RandomAlterOctaves(int lineIndex) {
var elements = this.ElementsOfLine(lineIndex);
//// int n = 0;
foreach (var element in elements.Where(element => element != null)) {
element.RandomAlterOctave();
}
}
#endregion
#region Public methods - Lines
///
/// Deletes the track.
///
/// The line number.
public void DeleteLine(int lineIndex) {
foreach (var bar in this.Bars) {
//// .Where(bar => bar != null)
if (lineIndex >= bar.Elements.Count) {
continue;
}
var element = bar.Elements[lineIndex];
bar.Elements.Remove(element);
}
}
///
/// Deletes the line.
///
/// The line identifier.
public void DeleteLine(Guid lineIdent) {
foreach (var bar in this.Bars) {
var element = bar.GetElement(lineIdent);
if (element != null) {
bar.Elements.Remove(element);
}
}
}
#endregion
#region Public methods - Bars
///
/// Creates bars according to harmonic definition in the given segment.
///
/// The changes.
public void SetHarmonicBasis(MusicalChanges changes) {
//// bool relative
if (changes == null) {
return;
}
var harmonicOrder = this.Context.Header.System.HarmonicOrder;
//// var templateStatus = new BarStatus();
int barNumberInMotive = 0;
foreach (var bar in this.Bars.Where(bar => bar != null && harmonicOrder > 0)) {
//// Musical block has harmonic background (so it is not rhythmic only)
if (changes.CurrentChange(0, bar.BarNumber, MusicalChangeType.Harmonic) is HarmonicChange dc) {
bar.ApplyHarmonicChange(dc);
}
//// var ms = (BarStatus)templateStatus.Clone();
//// ms.BarNumber = bar.BarNumber;
//// ms.LineIndex = 0;
if (bar.HasHarmonicMotive) {
barNumberInMotive = bar.BarNumber - bar.HarmonicStatus.BarNumber + 1;
var harmonicBar =
bar.HarmonicStatus.HarmonicMotive.HarmonicBarWithNumber(
barNumberInMotive, this.Context.Header.System);
var newHarmonicBar = (HarmonicBar)harmonicBar.Clone();
newHarmonicBar.BarNumber = bar.BarNumber;
bar.SetHarmonicBar(newHarmonicBar);
bar.NumberInHarmonicMotive = barNumberInMotive;
if (barNumberInMotive == 1 && bar.PreviousBar != null) {
bar.PreviousBar.IsLastInHarmonicMotive = true;
}
}
else {
//// throw new ArgumentException("No har.motive bar!");
var hmb1 = new HarmonicBar(barNumberInMotive, bar.BarNumber);
bar.SetHarmonicBar(hmb1);
}
//// 2016 this.BarStatus = ms;
}
foreach (var bar in this.Bars.Where(bar => bar != null)) {
bar.SetModalityToStructures(MusicalSettings.Singleton.MinimalModalityLevel);
}
}
#endregion
#region Public methods - Tempo
///
/// Set Tempo Events From.
///
/// Midi time from.
/// Midi time To.
/// The given tone tracks.
public void SetTempoEventsFrom(long timeFrom, long timeTo, List givenToneTracks) {
if (givenToneTracks.Count == 0) {
return;
}
var tempoEvents = new List();
var track0 = givenToneTracks[0];
if (track0 == null) {
return;
}
var originalEvents = track0.Sequence.GetTempoEvents(timeFrom, timeTo);
if (originalEvents != null) {
IEnumerable midiEvents = originalEvents as IList ?? originalEvents.ToList();
if (midiEvents.Any()) {
tempoEvents.AddRange(midiEvents);
}
}
//// Remove duplicity events
var selectedTempoEvents = new List();
MetaTempo lastEvent = null;
foreach (var ev in tempoEvents) {
var tev = ev as MetaTempo;
if (lastEvent != null && tev != null && tev.StartTime == lastEvent.StartTime &&
tev.Tempo == lastEvent.Tempo) {
continue;
}
selectedTempoEvents.Add(ev);
lastEvent = tev;
}
this.TempoEvents = selectedTempoEvents;
this.SetTempoStatusFromEvents(selectedTempoEvents);
}
#endregion
#region Public methods - Rhythm
///
/// Sets the line simple rhythm.
///
/// The line number.
/// The rhythmic order.
public void SetSimpleRhythm(int lineIndex, byte rhythmicOrder) {
var elements = this.ElementsOfLine(lineIndex);
if (elements == null) {
return;
}
var lineElements = elements as IList ?? elements.ToList();
var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rhythmicOrder);
const decimal rhythmNumber = 1;
var rstruct = new RhythmicStructure(rsystem, rhythmNumber);
foreach (var element1 in lineElements.Where(element1 => element1 != null)) {
element1.Status.RhythmicStructure = rstruct;
}
}
///
/// Sets the line rhythm to bars.
///
/// Index of the line.
/// The rhythmic structure.
[UsedImplicitly]
public void SetRhythmToBars(int lineIndex, RhythmicStructure rhythmicStructure) {
var lineElements = this.ElementsOfLine(lineIndex);
var elements = lineElements as IList ?? lineElements.ToList();
foreach (var element1 in elements.Where(element1 => element1 != null)) {
element1.Status.RhythmicStructure = rhythmicStructure;
}
}
///
/// Sets the simple rhythm to selected bars.
///
/// The line number.
/// The target rhythmic order.
/// Period in bars.
public void SetSimpleRhythmToBars(int lineIndex, byte targetRhythmicOrder, int period) {
var lineElements = this.ElementsOfLine(lineIndex);
var elements = lineElements as IList ?? lineElements.ToList();
var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, targetRhythmicOrder);
const decimal simpleRhythm = 1;
var rstruct = new RhythmicStructure(rsystem, simpleRhythm);
RhythmicStructure lastDefinedStruct = rstruct;
foreach (var element1 in elements.Where(element1 => element1 != null)) {
if (element1.Status.RhythmicStructure != null) {
lastDefinedStruct = element1.Status.RhythmicStructure;
}
element1.Status.RhythmicStructure =
element1.Point.BarNumber % period == 0 ? rstruct : lastDefinedStruct;
}
}
///
/// Sets the rhythm to bars.
///
/// Index of the line.
/// The line rhythm.
/// The target rhythmic order.
public void SetRhythmToBars(int lineIndex, LineRhythm lineRhythm, byte targetRhythmicOrder) {
switch (lineRhythm) {
case LineRhythm.SimpleOneTone: {
this.SetSimpleRhythmToBars(lineIndex, targetRhythmicOrder, 8);
}
break;
case LineRhythm.HarmonicShape: {
this.SetHarmonicRhythmToBars(lineIndex, 0, targetRhythmicOrder); //// 2 = combination
}
break;
case LineRhythm.HarmonicStructure: {
this.SetHarmonicRhythmToBars(lineIndex, 1, targetRhythmicOrder); //// 2 = combination
}
break;
}
}
///
/// Sets the line harmonic rhythm to bars.
///
/// The line number.
/// The variant.
/// The target rhythmic order.
public void SetHarmonicRhythmToBars(int lineIndex, byte variant, byte targetRhythmicOrder) {
var lineElements = this.ElementsOfLine(lineIndex);
var musicalEditorElements = lineElements as IList ?? lineElements.ToList();
var targetSystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, targetRhythmicOrder);
foreach (var element1 in musicalEditorElements.Where(element1 => element1 != null)) {
var bar = this.Bars[element1.Point.BarNumber - 1];
var rs0 = bar.HarmonicBar?.RhythmicStructure;
RhythmicStructure rhyStructure = null;
if (variant == 0 && rs0 != null) {
rhyStructure = rs0;
}
if (rhyStructure == null) {
bar.DetermineCommonRhythmicShape(true);
var rs1 = bar.CommonRhythmicStructure();
if (variant == 1 || variant == 0) {
rhyStructure = rs1;
}
else {
if (rs1.ToneLevel > 5 && rs0 != null) {
rhyStructure = rs0;
continue;
}
else {
rhyStructure = rs1;
}
}
}
var targetRhythmicStructure = rhyStructure.ConvertToSystem(targetSystem);
element1.Status.RhythmicStructure = targetRhythmicStructure;
//// element1.Status.RhythmicStructure = rs0 != null && rs0.ToneLevel > 3 ? rs1 : rs0;
//// element1.Status.RhythmicStructure = MathSupport.RandomNatural(10) < 5 ? rs0 : rs1;
}
}
#endregion
#region Status
///
/// Sets the melodic status from tones.
///
[UsedImplicitly]
public void SetStatusFromMusic() {
var changes = this.ExtractTempoChanges();
if (changes != null) {
var tempoChanges = changes.ToList();
TempoChange tempoStatus = null;
foreach (var bar in this.Bars) {
var tempoChange = (from c in tempoChanges where c.BarNumber == bar.BarNumber select c)
.FirstOrDefault();
if (tempoChange != null) {
tempoStatus = tempoChange;
}
if (tempoStatus == null) {
continue;
}
bar.ApplyTempoChange(tempoStatus);
}
}
for (int lineIndex = 0; lineIndex < this.Context.Header.NumberOfLines; lineIndex++) {
foreach (var bar in this.Bars) {
var element = (from elem in bar.Elements
where elem.Line != null && elem.Line.LineIndex == lineIndex
select elem).FirstOrDefault();
//// var element = bar.Elements[lineIndex]; //// !!!!!!!!! attention!
//// Rhythmical and melodical structure in bar
if (element == null) {
continue;
}
element.Status.BarNumber = bar.BarNumber;
element.Status.BeatLevel = (byte)element.Tones.Count;
element.Status.ToneLevel = (byte)element.Tones.CountOfMelTones;
var musicalTonesInBar = element.Tones;
if (musicalTonesInBar == null || musicalTonesInBar.Count == 0) {
continue;
}
//// Rhythmical structure in bar - from tones
RhythmicStructure rstruct =
musicalTonesInBar.DetermineRhythmicStructure(this.Context.Header.System.RhythmicOrder);
element.Status.RhythmicStructure = rstruct;
//// Melodic structure in bar - from tones
var isMelodic = element.Status.IsMelodic;
if (isMelodic) {
var melodicTonesInBar = element.SingleMelodicTones();
if (melodicTonesInBar != null && melodicTonesInBar.Any()) {
//// lastMelodicTone
var mstruct = melodicTonesInBar.DetermineMelodicStructure(null, bar.HarmonicBar);
if (mstruct == null) {
var system = new MelodicSystem(1, 1);
mstruct = new MelodicStructure(system, 1);
}
element.Status.MelodicStructure = mstruct;
}
}
if (element.Status.LineType == MusicalLineType.Rhythmic) {
element.Status.Instrument = new MusicalInstrument(musicalTonesInBar.FirstRhythmicInstrument);
}
if (element.Status.LineType == MusicalLineType.Melodic) {
element.Status.Instrument = new MusicalInstrument(musicalTonesInBar.FirstMelodicInstrument);
}
}
}
}
///
/// Extracts the simple changes.
///
public void SetBodyStatusFromTones() {
var setup = this.Context.Settings;
this.SetHarmonicStatusFromTones(3, setup.FullHarmonization);
//// Status of tracks
for (int lineIndex = 0; lineIndex < this.Context.Header.NumberOfLines; lineIndex++) {
foreach (var bar in this.Bars) {
var element = bar.Elements[lineIndex];
//// 2018/10 Only fixed tracks!?
//// 2018/10 element.Line.Status.LocalPurpose
if (element.Status.LocalPurpose == LinePurpose.Fixed) {
element.SetElementStatusFromTones();
}
}
}
}
///
/// Set Harmonic Status From Tones.
///
/// The given maximum tones in chord.
/// if set to true [given full harmonization].
public void SetHarmonicStatusFromTones(byte givenMaxTonesInChord, bool givenFullHarmonization) {
var numberOfBars = Math.Min(this.Context.Header.NumberOfBars, MusicalSettings.Singleton.MaxNumberOfBars);
this.Context.Header.NumberOfBars = numberOfBars;
var harmonicStreamAnalyzer = new HarmonicStreamAnalyzer(
this.Context.Header,
givenMaxTonesInChord,
givenFullHarmonization) {
SharpChordEdges = true,
HarmonicSpace = new HarmonicSpace(this.Context.Header.System.HarmonicSystem) {
ConsiderEnergyDecreaseInTime = false,
ConsiderImpulseBindings = false,
StrongImpulseBindings = false,
ConsiderContinuityBindings = false
}
};
foreach (MusicalBar bar in this.Bars) {
var harmonicBar = harmonicStreamAnalyzer.DetermineHarmonyInBar(bar);
//// var newHarmonicBar = (HarmonicBar)harmonicBar.Clone();
//// bar.HarmonicBar = newHarmonicBar;
bar.SetHarmonicBar(harmonicBar);
bar.HarmonicBar.BarNumber = bar.BarNumber;
}
}
#endregion
#region Changes
///
/// Extracts the simple changes.
///
/// Type of the given change.
///
/// Returns object.
///
public IEnumerable ExtractSimpleChanges(MusicalChangeType givenChangeType) {
var changes = new Collection();
for (int lineIndex = 0; lineIndex < this.Context.Header.NumberOfLines; lineIndex++) {
var lastOctave = -10;
var lastLoudness = -10;
var lastStaff = -10;
var lastVoice = -10;
var lastToneLevel = -10;
var lastBeatLevel = -10;
var lastRhythmicTension = -10;
byte lastInstrument = (byte)MidiMelodicInstrument.None;
foreach (var bar in this.Bars) {
if (bar.Elements.Count <= lineIndex) {
//// 2017/03 !?!?!
continue;
}
var element = bar.Elements[lineIndex];
if (element.Line == null) {
continue;
}
//// var isMelodic = element.Status.IsMelodic;
AbstractChange change;
//// *** Loudness ***
if (givenChangeType == MusicalChangeType.Loudness || givenChangeType == MusicalChangeType.All) {
var loudness = element.Status.Loudness;
if ((int)loudness != lastLoudness) {
change = new LoudnessChange(bar.BarNumber, element.Line.LineIndex) { LoudnessBase = loudness };
changes.Add(change);
lastLoudness = (int)loudness;
}
}
//// *** Energy ***
if (givenChangeType == MusicalChangeType.Energy || givenChangeType == MusicalChangeType.All) {
var rstruct = element.Status.RhythmicStructure;
if (rstruct != null && (rstruct.ToneLevel != lastToneLevel || rstruct.Level != lastBeatLevel ||
(byte)rstruct.FormalBehavior.Variance != lastRhythmicTension)) {
change = new EnergyChange(bar.BarNumber, element.Line.LineIndex) {
ToneLevel = rstruct.ToneLevel,
BeatLevel = rstruct.Level,
RhythmicTension = (byte)rstruct.FormalBehavior.Variance
};
changes.Add(change);
lastToneLevel = rstruct.ToneLevel;
lastBeatLevel = rstruct.Level;
lastRhythmicTension = (byte)rstruct.FormalBehavior.Variance;
}
}
//// *** StaffChange ***
if (givenChangeType == MusicalChangeType.Staff || givenChangeType == MusicalChangeType.All) {
var staff = element.Status.Staff;
var voice = element.Status.Voice;
if (staff != lastStaff || voice != lastVoice) {
change = new StaffChange(bar.BarNumber, element.Line.LineIndex) { Staff = staff, Voice = voice };
changes.Add(change);
}
lastStaff = staff;
lastVoice = voice;
}
//// *** Octaves ***
if (givenChangeType == MusicalChangeType.Octave || givenChangeType == MusicalChangeType.All) {
var octave = element.Status.Octave;
if ((int)octave != lastOctave) {
change = new OctaveChange(bar.BarNumber, element.Line.LineIndex) {
MusicalOctave = octave,
MusicalBand = MusicalProperties.BandTypeFromOctave(octave)
};
changes.Add(change);
lastOctave = (int)octave;
}
}
//// *** Instrument ***
if (givenChangeType == MusicalChangeType.Instrument || givenChangeType == MusicalChangeType.All) {
if (element.Status.Instrument == null) {
element.Status.Instrument = new MusicalInstrument(MidiMelodicInstrument.None);
}
var instrument = element.Status.Instrument.Number;
var channel = element.Line.MainVoice.Channel;
if (instrument != lastInstrument) {
change = new InstrumentChange(bar.BarNumber, element.Line.LineIndex) {
Instrument = element.Status.Instrument,
Channel = channel
};
changes.Add(change);
}
lastInstrument = instrument;
}
}
}
return changes;
}
#endregion
#region Private methods
///
/// Extracts the tempo changes.
///
///
/// Returns value.
///
private IEnumerable ExtractTempoChanges() {
//// cyclomatic complexity 10:15
//// var tracks = musicalBlock.MidiFile.AllTracks();
//// IEnumerable tempoEvents = tracks.GetTempoEvents();
var tempoEvents = this.TempoEvents;
//// Music Xml
if (tempoEvents == null) {
return null;
}
var tempoChanges = new List();
int lastTempo = 0, lastBarNumber = 0;
var header = this.Context.Header;
foreach (var ev in tempoEvents) {
MetaTempo tempoEvent = ev as MetaTempo;
if (tempoEvent == null) {
continue;
}
var currentTempo = tempoEvent.Tempo;
if (currentTempo > 0 && currentTempo != lastTempo) {
var metricGround = MusicalProperties.GetMetricGround(header.Metric.MetricBase);
var barDivision =
MusicalProperties.BarDivision(header.Division, header.Metric.MetricBeat, metricGround);
var barNumber = (int)Math.Floor((double)tempoEvent.StartTime / barDivision) + 1;
if (barNumber > lastBarNumber) {
var change = new TempoChange(barNumber) { TempoNumber = currentTempo };
tempoChanges.Add(change);
lastBarNumber = barNumber;
}
}
lastTempo = currentTempo;
}
return tempoChanges;
}
///
/// Fists playing element in track.
///
/// Index of the line.
///
/// Returns value.
///
private MusicalElement FistPlayingElementInTrack(int lineIdx) {
MusicalElement element = null;
foreach (var bar in this.Bars) {
var elem = bar.Elements[lineIdx];
if (elem?.Status == null || !elem.Status.HasInstrument) {
continue;
}
element = elem;
break;
}
return element;
}
#endregion
///
/// Sets the tempo status from events.
///
/// The tempo events.
private void SetTempoStatusFromEvents(List tempoEvents) {
//// 2019/02 taken from ConvertStripToBody
var h = this.Context.Header;
var barMidiDuration = MusicalProperties.BarMidiDuration(h.System.RhythmicOrder, h.Metric.MetricBeat, h.Metric.MetricGround, h.Division);
if (tempoEvents != null) {
var midiEvents = tempoEvents as IList;
if (midiEvents.Any()) {
int currentTempoNumber = 0;
foreach (var bar in this.Bars) {
//// tev.BarNumber == bar.BarNumber ?
if ((from tev in midiEvents
where tev.StartTime <= bar.TimePoint && bar.TimePoint <= tev.StartTime + barMidiDuration
select tev).FirstOrDefault() is MetaTempo metaTempo) {
currentTempoNumber = metaTempo.Tempo;
}
bar.TempoNumber = currentTempoNumber;
}
}
}
}
#region Sorting (internal only)
///
/// Enables comparison of two events based on delta times.
///
///
private sealed class BarComparer : IComparer
{
///
/// The line type
///
private readonly MusicalLineType lineType;
///
/// Initializes a new instance of the class.
///
/// Type of the given line.
public BarComparer(MusicalLineType givenLineType) {
this.lineType = givenLineType;
}
#region Implementation of IComparer
///
/// Compares two MidiEvents based on delta times.
///
/// The first MidiEvent to compare.
/// The second MidiEvent to compare.
///
/// Returns -1 if x.StartTime is larger, 0 if they're the same, 1 otherwise.
///
///
/// x
/// or
/// y
///
public int Compare(MusicalBar x, MusicalBar y) {
var barX = x;
var barY = y;
// Make sure they're valid
if (barX == null) {
throw new ArgumentNullException(nameof(x));
}
if (barY == null) {
throw new ArgumentNullException(nameof(y));
}
// Compare the delta times
return barX.ValueOfTicks(this.lineType).CompareTo(barY.ValueOfTicks(this.lineType)); //// CountOfTones
}
#endregion
}
#endregion
}
}